package com.testing.sunnycommon.service.impl;

import com.testing.sunnycommon.annotations.SunnyAdvice;
import com.testing.sunnycommon.automation.ApiKeywords;
import com.testing.sunnycommon.automation.WebKeywords;
import com.testing.sunnycommon.automation.model.ApiResult;
import com.testing.sunnycommon.automation.model.AutoResult;
import com.testing.sunnycommon.automation.model.StatusType;
import com.testing.sunnycommon.model.*;
import com.testing.sunnycommon.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Classname SunnyRunServiceImpl
 * @Description 类型说明
 * @Date 2023/5/19 20:09
 * @Created by 特斯汀Roy
 */
@Slf4j
@Service("sunnyRunService")
public class SunnyRunServiceImpl implements SunnyRunService {

    @Resource
    ApiKeywords apiKeywords;

    @Resource
    WebKeywords webKeywords;

    @Resource
    SunnyVariableService sunnyVariableService;

    @Resource
    SunnyProjectService sunnyProjectService;

    @Resource
    SunnyCaseService sunnyCaseService;

    @Resource
    SunnyResultService sunnyResultService;

    @Resource
    SunnyResultDetailService sunnyResultDetailService;

    @SunnyAdvice("自动化测试执行完成")
    @Async
    @Override
    public Result runTest(Long projectId) {
        List<SunnyModule> moduleList = sunnyProjectService.listModuleInProject(projectId);

        //开始一次运行要记录的信息：
        SunnyResult sunnyResult = new SunnyResult();
        sunnyResult.setId(0L);
        sunnyResult.setProjectId(projectId);
        sunnyResult.setBuildOrder(sunnyResultService.getMaxBuildOrderOfProject(projectId) + 1);
        sunnyResult.setModuleCount((long) (moduleList.size()));
        //获取项目信息中已经记录的caseCount和stepCount
        SunnyProject sunnyProject = sunnyProjectService.getById(projectId);
        sunnyResult.setStepCount(sunnyProject.getStepCount());
        sunnyResult.setCaseCount(sunnyProject.getCaseCount());
        //开始时间设置
        sunnyResult.setExecuteTime(LocalDateTime.now());
        sunnyResult.setExecutor("执行人");
        log.info("开始执行自动化测试项目【"+sunnyProject.getName()+"】");
        log.info("要执行的模块包括：" + moduleList);

        //由于后续记录resultDetail需要resultId，所以在开始执行之前，先存一下result信息，从而产生一个resultId
        sunnyResultService.save(sunnyResult);
        //获取最后一个sunnyResult数据，也就是新插入的数据，生成resultId
        SunnyResult lastSunnyResult = sunnyResultService.getLastSunnyResult();
        Long resultId = lastSunnyResult.getId();
        //重新设置当前操作的result对象的id为这个新Id
        sunnyResult.setId(resultId);

        //项目级别的用例成功数统计
        Long totalPassCaseCount = 0L;
        Long totalFailCaseCount = 0L;
        Long totalSkippedCaseCount = 0L;

        //遍历模块，操作结果的时候，要做的就是为模块名字编号，因此要更新存储moduleName的map计数
        moduleNameCountMap = new HashMap<>();
        for (SunnyModule sunnyModule : moduleList) {
            //确定要执行的关键字类型
            Object keywords = sunnyModule.getType().equals("web") ? webKeywords : apiKeywords;
            //获取关键字类
            Class<?> keywordsClass = keywords.getClass();
            //为模块重新命名
            String dupliacteModuleName = generateDuplicateModuleName(sunnyModule.getName());
            log.info("执行模块" + dupliacteModuleName + "，类型是" + sunnyModule.getType());
            //遍历模块中的用例和步骤
            //遍历用例，需要重新写sql语句
            List<SunnyCase> sunnyCaseList = sunnyCaseService.listCasesInOrder(sunnyModule.getId());
            //用例级别的处理
            for (SunnyCase sunnyCase : sunnyCaseList) {
                //每一条用例不管是步骤还是用例，都记录成一条resultDetail
                SunnyResultDetail sunnyCaseResultDetail = new SunnyResultDetail();
                //设置id和resultid
                sunnyCaseResultDetail.setId(0L);
                sunnyCaseResultDetail.setResultId(resultId);
                //设置其它的已知信息，因为是用例，所以没有关键字等信息，只用设置caseName以及moduleName
                sunnyCaseResultDetail.setModuleName(dupliacteModuleName);
                sunnyCaseResultDetail.setCaseName(sunnyCase.getCaseName());
                sunnyCaseResultDetail.setParentId(sunnyCase.getParentId());
                //步骤的parentid需要用用例的id，所以先存进去，生成一个id，让整体的顺序直接按照id排序，不需要重新再排列。
                sunnyResultDetailService.save(sunnyCaseResultDetail);
                SunnyResultDetail lastSunnyResultDetail = sunnyResultDetailService.getLastSunnyResultDetail();
                Long lastSunnyResultDetailId = lastSunnyResultDetail.getId();
                sunnyCaseResultDetail.setId(lastSunnyResultDetailId);

                //遍历用例中的步骤,parentId是当前用例的id。
                List<SunnyCase> sunnyStepList = sunnyCaseService.listStepsInOrder(sunnyModule.getId(), sunnyCase.getId());

                //还剩统计数字需要计数
                Long stepTotalCount = (long) sunnyStepList.size();
                //通过数
                Long stepPassCount = 0L;
                //失败数
                Long stepFailCount = 0L;
                //跳过数
                Long stepSkippedCount = 0L;

                for (SunnyCase sunnyStep : sunnyStepList) {
                    //创建每个步骤的结果详情,先写入已知内容
                    SunnyResultDetail sunnyStepResultDetail = new SunnyResultDetail();
                    sunnyStepResultDetail.setId(0L);
                    sunnyStepResultDetail.setResultId(resultId);
                    sunnyStepResultDetail.setParentId(lastSunnyResultDetailId);
                    sunnyStepResultDetail.setModuleName(dupliacteModuleName);
                    sunnyStepResultDetail.setCaseName(sunnyCase.getCaseName());
                    sunnyStepResultDetail.setStepName(sunnyStep.getStepName());
                    sunnyStepResultDetail.setKeywords(sunnyStep.getKeywords());
                    sunnyStepResultDetail.setParam1(sunnyVariableService.replaceVariables(projectId, sunnyStep.getParam1()));
                    sunnyStepResultDetail.setParam2(sunnyVariableService.replaceVariables(projectId, sunnyStep.getParam2()));

                    //查找keywords名称相同的关键字方法，执行步骤
                    Method declaredMethod;
                    AutoResult autoResult = AutoResult.skipped("本次步骤【" + sunnyStep.getStepName() + "：" + sunnyStep.getKeywords() + "】执行被跳过了");
                    try {
                        //试试是不是无参的方法
                        declaredMethod = keywordsClass.getDeclaredMethod(sunnyStep.getKeywords());
                        //反射
                        autoResult = (AutoResult) (declaredMethod.invoke(keywords));
                    } catch (Exception e) {
                        try {
                            //调用失败，就不是无参方法，试试一个参数的
                            declaredMethod = keywordsClass.getDeclaredMethod(sunnyStep.getKeywords(), String.class);
                            //反射调用，在传参的时候，先使用variableService完成参数中的变量替换
                            autoResult = (AutoResult) (declaredMethod.invoke(keywords,
                                    sunnyVariableService.replaceVariables(projectId, sunnyStep.getParam1())
                            ));
                        } catch (Exception e1) {
                            try {
                                declaredMethod = keywordsClass.getDeclaredMethod(sunnyStep.getKeywords(), String.class, String.class);
                                //反射调用，在传参的时候，先使用variableService完成参数中的变量替换
                                autoResult = (AutoResult) (declaredMethod.invoke(keywords,
                                        sunnyVariableService.replaceVariables(projectId, sunnyStep.getParam1()),
                                        sunnyVariableService.replaceVariables(projectId, sunnyStep.getParam2())
                                ));
                            } catch (Exception e2) {
                                autoResult.setStatus(StatusType.SKIPPED);
                                autoResult.setInfo("没有找到" + sunnyStep.getKeywords() + "这个关键字，请检查用例输入");
                                log.error("在关键字类中没有找到" + sunnyStep.getKeywords() + "所对应的方法，0/1/2个参数都不行。", e2.fillInStackTrace());
                            }
                        }
                    }

                    //执行完成之后，设置执行结果
                    sunnyStepResultDetail.setStatus(autoResult.getStatus());
                    if (autoResult.getStatus().equals(StatusType.PASS)) {
                        stepPassCount++;
                    } else if (autoResult.getStatus().equals(StatusType.FAIL)) {
                        stepFailCount++;
                    } else {
                        stepSkippedCount++;
                    }
                    sunnyStepResultDetail.setDetail(autoResult.getInfo());
                    sunnyStepResultDetail.setShotPath(autoResult.getScreenShotPath());
                    //结束步骤执行，写入数据库表中
                    sunnyResultDetailService.save(sunnyStepResultDetail);
                }

                //sunnyCase循环结束，存储用例级别resultDetail
                //如果用例中的步骤数和通过步骤数相等，那么这条用例是通过的，并且项目中总的用例通过数+1
                if (stepTotalCount == stepPassCount) {
                    sunnyCaseResultDetail.setStatus(StatusType.PASS);
                    totalPassCaseCount++;
                } else if (stepTotalCount == stepSkippedCount) {
                    sunnyCaseResultDetail.setStatus(StatusType.SKIPPED);
                    totalSkippedCaseCount++;
                } else {
                    //只要不是全通过或者全跳过，即使有通过有跳过，没有失败，也认为失败。
                    sunnyCaseResultDetail.setStatus(StatusType.FAIL);
                    totalFailCaseCount++;
                }
                //结束用例执行存储用例级别的数据
                //设置详情为  步骤通过数/总数
                sunnyCaseResultDetail.setDetail(stepPassCount + "/" + stepTotalCount);
                //更新用例数据
                sunnyResultDetailService.updateById(sunnyCaseResultDetail);
            }
        }

        //设置项目级别统计数据
        sunnyResult.setPassCount(totalPassCaseCount);
        sunnyResult.setFailCount(totalFailCaseCount);
        sunnyResult.setSkippedCount(totalSkippedCaseCount);
        //设置结束时间
        sunnyResult.setFinishTime(LocalDateTime.now());
        //存到数据库里面
        sunnyResultService.updateById(sunnyResult);
        return Result.success(sunnyProject.getName() + "项目自动化测试执行完成", sunnyResult);
    }

    //记录模块名出现的次数，方便拼接
    private Map<String, Integer> moduleNameCountMap;

    /**
     * 完成模块名的序号拼接
     *
     * @param moduleName 模块名
     * @return
     */
    private String generateDuplicateModuleName(String moduleName) {
        //判断存储的时候是否已经存储过这个moduleName了,进行存储
        if (moduleNameCountMap.containsKey(moduleName)) {
            //如果有，次数更新为 +1
            moduleNameCountMap.replace(moduleName, moduleNameCountMap.get(moduleName) + 1);
        } else {
            //如果没有，存进去，并且次数为0
            moduleNameCountMap.put(moduleName, 0);
        }
        //取，如果只出现了一次，那就不用拼接数字，出现多次，就拼接
        if (moduleNameCountMap.get(moduleName) == 0) {
            return moduleName;
        } else {
            return moduleName + moduleNameCountMap.get(moduleName);
        }
    }


    private void runShopCustomer() {
        WebKeywords webKeywords = new WebKeywords();
        webKeywords.openBrowser("E:\\AutoTools\\chromedriver\\113\\chromedriver_win32\\chromedriver.exe");
        webKeywords.visitWeb("http://www.testingedu.com.cn:8000/index.php");
        webKeywords.hover("//a[text()='家具家居']");
        webKeywords.click("//a[text()='餐']");

    }

    private void runShopAdmin() {
        WebKeywords webKeywords = new WebKeywords();
        webKeywords.openBrowser("E:\\AutoTools\\chromedriver\\113\\chromedriver_win32\\chromedriver.exe");
        webKeywords.visitWeb("http://www.testingedu.com.cn:8000/index.php/Admin/Index/index");
        webKeywords.input("input[name='username']", "admin");
        webKeywords.input("input[name='password']", "123456");
        webKeywords.input("input[name='vertify']", "1");
        webKeywords.click("input[name='submit']");
        webKeywords.click("//a[text()='商城']");
        webKeywords.click("//a[text()='商品列表']");
        webKeywords.switchIframe("workspace");
        webKeywords.click("//span[text()='添加商品']");
        String goodsName = "平台测试添加商品" + new SimpleDateFormat("HH-mm-ss").format(new Date());
        webKeywords.input("//input[@name='goods_name']", goodsName);
        webKeywords.selectByVisibleText("cat_id", "家居");
        webKeywords.selectByVisibleText("cat_id_2", "厨具");
        webKeywords.selectByVisibleText("cat_id_3", "餐具");
        webKeywords.input("//input[@name='shop_price']", "300");
        webKeywords.input("//input[@name='market_price']", "500");
        webKeywords.click("is_free_shipping_label_1");
        webKeywords.click("//span[@class=\"type-file-box\"]/input[@class='type-file-file']");
        webKeywords.switchIframe("//iframe[contains(@id,'layui-layer-iframe')]");
        webKeywords.input("//div[text()='点击选择文件']/following-sibling::div/input", "E:\\QSwork\\素材\\微信二维码.png");
        webKeywords.click("//div[@class=\"saveBtn\"]");
        webKeywords.switchParentFrame();
        webKeywords.click("//a[text()='确认提交']");
        webKeywords.assertElementContainsText("//div[@id='flexigrid']/table//tr[1]/td[4]/div", goodsName);

        webKeywords.closeBrowser();
        log.info("自动化测试结束啦");
    }

    private void testApi() {
        //创建一个用于发包的httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        //创建需要使用的http请求方法，填写url
        HttpPost loginAdmin = new HttpPost("http://www.testingedu.com.cn:8000/index.php?m=Admin&c=Admin&a=login&t=0.3237855237174614");
        //请求体创建的时候，注意记得带上字符集编码设置
        StringEntity loginParams = new StringEntity("username=admin&password=123456&vertify=1", "utf-8");
        loginParams.setContentType("application/x-www-form-urlencoded;charset=utf-8");
        loginParams.setContentEncoding("utf-8");
        //设置请求报文的请求实体为loginParams
        loginAdmin.setEntity(loginParams);

        try {
            CloseableHttpResponse loginResponse = client.execute(loginAdmin);
            log.info("返回报文是" + loginResponse);
            ApiResult apiResult = new ApiResult();
            apiResult.setStatusCode(loginResponse.getStatusLine().getStatusCode());
            apiResult.setResponseHeaders(loginResponse.getAllHeaders());
            //解析返回体
            HttpEntity responseEntity = loginResponse.getEntity();
            log.info("返回体是" + responseEntity);
            //把返回体中的文字信息处理为字符串
            String responseText = EntityUtils.toString(responseEntity, "utf-8");
            apiResult.setResponseText(responseText);
            log.info("最终的返回结果是" + apiResult);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void runApiCookie() {
        ApiKeywords api = new ApiKeywords();

        AutoResult autoResult = api.testPostUrl("http://www.testingedu.com.cn:8000/index.php?m=Admin&c=Admin&a=login&t=0.3237855237174614", "username=admin&password=123456&vertify=1");
        log.info("【登录】步骤的结果是" + autoResult.toString());
        AutoResult autoResult1 = api.assertStatusCode("404");
        log.info("【断言状态码】步骤的结果是" + autoResult1.toString());
        AutoResult autoResult2 = api.assertResponseRegex("\\\\/index.php\\\\/(.*?)\\\\/Index\\\\/index", "Admin");
        log.info("【基于正则断言】步骤的结果是" + autoResult2.toString());
        AutoResult autoResult3 = api.assertResponseJson("$.status", "1");
        log.info("【基于jsonPath解析断言】步骤的结果是" + autoResult3.toString());

        AutoResult autoResult4 = api.testPostFile("http://www.testingedu.com.cn:8000/index.php/Admin/Ueditor/imageUp/savepath/goods/pictitle/banner/dir/images", "{\"text\":{\"id\":\"WU_FILE_0\",\"size\":\"11988\",\"type\":\"image/png\"},\"file\":{\"file\":\"E:\\\\QSwork\\\\素材\\\\图片素材\\\\Jmeter标题2.jpg\"}}");
        log.info("【使用cookie的时候上传文件】的请求结果是" + autoResult4);
        AutoResult success = api.assertResponseJson("$.state", "SUCCESS");
        log.info("【使用cookie的时候上传文件】的断言结果是" + success);

        //不使用cookie的异常场景
        api.notUseCookie();
        AutoResult autoResult5 = api.testPostFile("http://www.testingedu.com.cn:8000/index.php/Admin/Ueditor/imageUp/savepath/goods/pictitle/banner/dir/images", "{\"text\":{\"id\":\"WU_FILE_0\",\"size\":\"11988\",\"type\":\"image/png\"},\"file\":{\"file\":\"E:\\\\QSwork\\\\素材\\\\图片素材\\\\Jmeter标题2.jpg\"}}");
        log.info("【不使用cookie上传文件】的请求结果是" + autoResult5);
        AutoResult plsLogin = api.assertResponseRegex("<i class=\"litps modifyfail\"></i>(.*?)\\s+</div>", "请先登录");
        log.info("【不使用cookie上传文件】的断言结果是" + plsLogin);


        //使用cookie的正常场景
        api.useCookie();
        AutoResult autoResult6 = api.testPostFile("http://www.testingedu.com.cn:8000/index.php/Admin/Ueditor/imageUp/savepath/goods/pictitle/banner/dir/images", "{\"text\":{\"id\":\"WU_FILE_0\",\"size\":\"11988\",\"type\":\"image/png\"},\"file\":{\"file\":\"E:\\\\QSwork\\\\素材\\\\图片素材\\\\Jmeter标题2.jpg\"}}");
        log.info("【使用cookie的时候上传文件】的请求结果是" + autoResult6);
        AutoResult success2 = api.assertResponseJson("$.state", "SUCCESS");
        log.info("【使用cookie的时候上传文件】的断言结果是" + success2);
    }

    private void runApiToken() {
        //平台后台登录之后获取token
        AutoResult jsonLoginResult = apiKeywords.testPostJson(
                sunnyVariableService.replaceVariables(1L, "{{url}}/user/login"), "{\"username\":\"admin\",\"password\":\"123456\"}");
        log.info("【平台端登录】的结果是" + jsonLoginResult.toString());

//        String tokenValue = "";
//        //遍历当前返回结果中的返回头，找到Authorization，存储为token的值
//        for (Header responseHeader : apiKeywords.getApiResult().getResponseHeaders()) {
//            if ("Authorization".equals(responseHeader.getName())) {
//                tokenValue = responseHeader.getValue();
//            }
//        }
        apiKeywords.saveResponseHeaderVar("author", "Authorization");


        apiKeywords.addHeader("Authorization", sunnyVariableService.replaceVariables(1L, "{{author}}"));

        AutoResult updateModuleResult = apiKeywords.testPutJson(sunnyVariableService.replaceVariables(1L, "{{url}}/module"), "{\"id\":63,\"moduleName\":\"接口自动化test\",\"isEdit\":true,\"isActive\":true}");
        log.info("【更新模块】请求结果是" + updateModuleResult);

        apiKeywords.notUseHeader();
        AutoResult deleteModuleResult = apiKeywords.testDelete(sunnyVariableService.replaceVariables(1L, "{{url}}/module/63"));
        log.info("【删除模块】的请求结果是" + deleteModuleResult);
    }

    private void testVariables() {
        apiKeywords.saveVariable("肉一", "可爱");
        apiKeywords.saveVariable("token", "token值");
        String replaced = sunnyVariableService.replaceVariables(1L, "roy老师的特点是{{肉一}},token值是{{token}},土匪的值是{{土匪}}");
        log.error(replaced);
        runApiToken();
    }

}
